/***********************************************************************
 * Module:  ActiveConnecting.java
 * Author:  Nikola
 * Purpose: Defines the Class ActiveConnecting
 ***********************************************************************/

package genedit.controller;

import genedit.gui.GToolbar;
import genedit.gui.exceptions.ElementNePostoji;
import genedit.model.Workspace;
import genedit.model.graph.BreakPoint;
import genedit.model.graph.ConnectionPoint;
import genedit.model.graph.GraphElement;
import genedit.model.graph.Link;
import genedit.model.graph.Property;
import genedit.model.graph.classModel.ClassModel;
import genedit.model.graph.classModel.GAttributes;
import genedit.model.graph.classModel.GMethod;
import genedit.model.graph.classModel.GMethods;
import genedit.model.graph.classModel.GeneralizationLink;
import genedit.model.graph.classModel.InterfaceModel;
import genedit.view.graph.ElementPainter;
import genedit.view.graph.GraphWorkArea;
import genedit.view.graph.LinkPainter;

import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

/**
 * stane u koje se ulazi za vreme povezivanja elemenata
 * 
 * @author Igic
 * 
 */
public class ActiveConnecting extends State {
	/** @param context */
	private Link link;

	public ActiveConnecting(GraphWorkArea context, Link link) {
		super(context);
		this.link = (Link) link;
	}

	protected void deleteTempLink() {
		MoveCommand.breakpointsMementoTranslator=null;
		DeleteCommand delCommand = new DeleteCommand(false);
		// obrisi vezu elementa sa vezom
		link.getStart().removeStarting(link);
		delCommand.getDeletedElements().add(link);
		delCommand.setDocument(context.getView());
		context.getView().getModel().getInvoker().executeCommand(delCommand);
	}

	// zavrsi iscrtavanje veze
	protected void finishConnecting(ElementPainter elementPainter, Point position) {
		GraphElement element = elementPainter.getElement();
		ConnectionPoint cp = new ConnectionPoint(position);
		link.setEnd(cp);
		element.addConnectionPoints(cp);
		cp.setParent(element);
		cp.addEnding(link);

		if (link.getBreakPoints().size() == 0) {
			link.findPath();
			link.rectLink();
			link.addIntersectionPoints();
		} else {
			link.rectLink();
			link.addIntersectionPoints();
		}

		AddCommand addCommand = new AddCommand(true);
		addCommand.getAddedElements().add(link);
		addCommand.setDocument(context.getView());
		context.getView().getModel().getInvoker().executeCommand(addCommand);
	}
	
	protected void addInheritedProperties() {
					for (Property p : link.getEnd().getParent().getProperties()) {
						if(p.getKey().equals("methods")){
							for (Property p1 : link.getStart().getParent().getProperties())
								if(p1.getKey().equals("methods")){
									for(GMethod method : ((GMethods)p.getValue()).getMethods())
										if(!((GMethods)p1.getValue()).getMethods().contains(method) && !((GMethods)p1.getValue()).getInheritedMethods().contains(method)){
											if(!((GMethods)p1.getValue()).getMethods().contains(method)&& link.getEnd().getParent() instanceof ClassModel &&
													!((GMethods)p1.getValue()).getInheritedMethods().contains(method))
												((GMethods)p1.getValue()).getInheritedMethods().add(method);
											else if(!((GMethods)p1.getValue()).getMethods().contains(method)&& link.getEnd().getParent() instanceof InterfaceModel &&
													!((GMethods)p1.getValue()).getInheritedMethods().contains(method))
												((GMethods)p1.getValue()).getMethods().add(method);
									
										}
									for(GMethod method : ((GMethods)p.getValue()).getInheritedMethods()){
										if(!((GMethods)p1.getValue()).getMethods().contains(method)&& link.getEnd().getParent() instanceof ClassModel &&
												!((GMethods)p1.getValue()).getInheritedMethods().contains(method))
											((GMethods)p1.getValue()).getInheritedMethods().add(method);
										else if(!((GMethods)p1.getValue()).getMethods().contains(method)&& link.getEnd().getParent() instanceof InterfaceModel &&
												!((GMethods)p1.getValue()).getInheritedMethods().contains(method))
											((GMethods)p1.getValue()).getMethods().add(method);
								
									}
								}
						}
						else {
							for (Property p1 : link.getStart().getParent().getProperties())
								if(p1.getKey().equals("attributes")){
									for(String attribute : ((GAttributes)p.getValue()).getAtributeKeys())
										if(!((GAttributes)p1.getValue()).getAttributes().containsKey(attribute) && !((GAttributes)p1.getValue()).getInheritedAttributes().containsKey(attribute))
										try {													
											((GAttributes)p1.getValue()).getInheritedAttributes().put(attribute, ((GAttributes)p.getValue()).getAtribute(attribute));
										} catch (ElementNePostoji e) {
											// TODO Auto-generated catch block
											e.printStackTrace();
										}
									for(String attribute : ((GAttributes)p.getValue()).getInheritedAttributes().keySet())
										if(!((GAttributes)p1.getValue()).getAttributes().containsKey(attribute) && !((GAttributes)p1.getValue()).getInheritedAttributes().containsKey(attribute))													
											((GAttributes)p1.getValue()).getInheritedAttributes().put(attribute, ((GAttributes)p.getValue()).getInheritedAttributes().get(attribute));
									
								}
						}
					}
	}
	
	protected void cyrcle(GraphElement element, GraphElement destination) {

			for(ConnectionPoint cp : element.getConnectionPoints()) 
				for(Link link : cp.getEnding()) 
					if(link instanceof GeneralizationLink){
					if(link.getStart().getParent().equals(destination))
					{
						deleteTempLink();
						returnToConnectState();
						return;
					}
					else {
						if(context.getView().getModel().getElements().contains(link))
						cyrcle(link.getStart().getParent(), destination);
					}
					}
			
	}
	
	protected boolean hasClassInherited(GraphElement element){
		for(ConnectionPoint cp : element.getConnectionPoints()) 
			for(Link link : cp.getStarting()) {
				if(link instanceof GeneralizationLink && link.getEnd()!=null &&link.getEnd().getParent() instanceof ClassModel)
					if(context.getView().getModel().getElements().contains(link))
					return true;
			}
		return false;
	}

	protected void returnToConnectState() {
		GraphWorkArea workArea = (GraphWorkArea) Workspace.getInstance().getActiveDocument()
				.getWorkArea();
		if (GToolbar.getInstance().getPolyLine().isSelected()) {
			workArea.getController().setCurrentState(new ConnectState(context, new LinkCreator()));
		} else if (GToolbar.getInstance().getAssociationLink().isSelected()) {
			workArea.getController().setCurrentState(new ConnectState(context, new AssociationCreator()));
		} else if (GToolbar.getInstance().getGeneralizationLink().isSelected()) {
			workArea.getController().setCurrentState(new ConnectState(context, new GeneralizationCreator()));
		} else if (GToolbar.getInstance().getAggregationLink().isSelected()) {
			workArea.getController().setCurrentState(new ConnectState(context, new AggregationCreator()));
		} else if (GToolbar.getInstance().getCompositionLink().isSelected()) {
			workArea.getController().setCurrentState(new ConnectState(context, new CompositionCreator()));
		} 
	}

	/**
	 * @param e
	 */
	public void mousePressed(MouseEvent e) {
		Point position = e.getPoint();
		context.pointToUserSpace(position);
		if (e.getButton() == MouseEvent.BUTTON3) {
			deleteTempLink();
			returnToConnectState();
			return;
		}
		if (e.getButton() != MouseEvent.BUTTON1) {
			return;
		}
		ElementPainter elementPainter = context.getView().getElementAtPosition(position);
		if (elementPainter != null && !(elementPainter instanceof LinkPainter)) {
			// zavrsavanje iscrtavanja
			if((link instanceof GeneralizationLink) && (((((GeneralizationLink)link).getStart().getParent() instanceof InterfaceModel)
					&& elementPainter.getElement() instanceof ClassModel)|| hasClassInherited(((GeneralizationLink)link).getStart().getParent()))){
				deleteTempLink();
				returnToConnectState();
				return;
			}
			else {
			if(link instanceof GeneralizationLink)
				cyrcle(link.getStart().getParent(), elementPainter.getElement());
			if(context.getView().getModel().getElements().contains(link)){
			finishConnecting(elementPainter, position);
			addInheritedProperties();
			returnToConnectState();
			}
			}
		} else {
			// dodaj break
			link.addBreakPoints(new BreakPoint(position));
		}
	}

	/** @param e */
	public void mouseReleased(MouseEvent e) {
		// ne registrovati dogadjaj ako je vec postavljen neki breakpoint
		if (link.getBreakPoints().size() > 1) {
			return;
		}
		Point position = e.getPoint();
		context.pointToUserSpace(position);
		ElementPainter elementPainter = context.getView().getElementAtPosition(position);
		if (elementPainter != null && !(elementPainter instanceof LinkPainter)) {
			if (elementPainter.getElement().equals(link.getStart().getParent())) {
				// povezano sa istim elementom
				deleteTempLink();
				returnToConnectState();
			} else {
				link.removeAllBreakPoints();
				if((link instanceof GeneralizationLink) && (((((GeneralizationLink)link).getStart().getParent() instanceof InterfaceModel)
				&& elementPainter.getElement() instanceof ClassModel)|| (hasClassInherited(((GeneralizationLink)link).getStart().getParent()) && 
				elementPainter.getElement() instanceof ClassModel))){
					deleteTempLink();
					returnToConnectState();
					return;
				}
				else{
				if(link instanceof GeneralizationLink)
					cyrcle(link.getStart().getParent(), elementPainter.getElement());	
				if(context.getView().getModel().getElements().contains(link)){
				finishConnecting(elementPainter, position);
				addInheritedProperties();
				returnToConnectState();
				}
				}
			}
		} else {
			// imamo breakpoint
			link.addBreakPoints(new BreakPoint(position));
		}
	}

	/** @param e */
	public void mouseDragged(MouseEvent e) {
		mouseMoved(e);
	}

	/** @param e */
	public void keyPressed(KeyEvent e) {
		// TODO: implement
	}

	public void mouseMoved(MouseEvent e) {
		if (link != null) {
			Point position = e.getPoint();
			context.pointToUserSpace(position);

			// pomeranje misa, azuriranje lokacije poslednje tacke
			if (link.getBreakPoints().size() == 0)
				link.addBreakPoints(new BreakPoint(position));
			else
				link.getLastPoint().setPosition(position);
			context.getView().getWorkArea().repaint();
		}
	}

}